package mz.co.research.client.view;

import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.LazyPanel;
import com.google.gwt.user.client.ui.Widget;

public abstract class ContentWidget extends LazyPanel implements
		HasValueChangeHandlers<String> {

	/**
	 * Generic callback used for asynchronously loaded data.
	 * 
	 * @param <T>
	 *            the data type
	 */
	public static interface Callback<T> {
		void onError();

		void onSuccess(T value);
	}

	/**
	 * A description of the example.
	 */
	private final String description;

	/**
	 * True if this example has associated styles, false if not.
	 */
	private final boolean hasStyle;

	/**
	 * The name of the example.
	 */
	private final String name;

	/**
	 * The view that holds the name, description, and example.
	 */
	private ContentWidgetView view;

	/**
	 * Whether the demo widget has been initialized.
	 */
	private boolean widgetInitialized;

	/**
	 * Whether the demo widget is (asynchronously) initializing.
	 */
	private boolean widgetInitializing;

	/**
	 * Construct a {@link ContentWidget}.
	 * 
	 * @param name
	 *            the name of the example
	 * @param description
	 *            a description of the example
	 * @param hasStyle
	 *            true if the example has associated styles
	 * @param rawSourceFiles
	 *            the list of raw source files to include
	 */
	public ContentWidget(String name, String description, boolean hasStyle) {
		this.name = name;
		this.description = description;
		this.hasStyle = hasStyle;
	}

	public HandlerRegistration addValueChangeHandler(
			ValueChangeHandler<String> handler) {
		return addHandler(handler, ValueChangeEvent.getType());
	}

	@Override
	public void ensureWidget() {
		super.ensureWidget();
		ensureWidgetInitialized();
	}

	/**
	 * Get the description of this example.
	 * 
	 * @return a description for this example
	 */
	public final String getDescription() {
		return description;
	}

	/**
	 * Get the name of this example to use as a title.
	 * 
	 * @return a name for this example
	 */
	public final String getName() {
		return name;
	}

	/**
	 * Check if the widget should have margins.
	 * 
	 * @return true to use margins, false to flush against edges
	 */
	public boolean hasMargins() {
		return true;
	}

	/**
	 * Returns true if this widget has a style section.
	 * 
	 * @return true if style tab available
	 */
	public final boolean hasStyle() {
		return hasStyle;
	}

	/**
	 * When the widget is first initialized, this method is called. If it
	 * returns a Widget, the widget will be added as the first tab. Return null
	 * to disable the first tab.
	 * 
	 * @return the widget to add to the first tab
	 */
	public abstract Widget onInitialize();

	/**
	 * Called when initialization has completed and the widget has been added to
	 * the page.
	 */
	public void onInitializeComplete() {
	}

	protected abstract void asyncOnInitialize(
			final AsyncCallback<Widget> callback);

	/**
	 * Initialize this widget by creating the elements that should be added to
	 * the page.
	 */
	@Override
	protected final Widget createWidget() {
		view = new ContentWidgetView(hasMargins());
		view.setName(getName());
		view.setDescription(getDescription());
		return view;
	}

	@Override
	protected void onLoad() {
		ensureWidget();
	}

	/**
	 * Ensure that the demo widget has been initialized. Note that
	 * initialization can fail if there is a network failure.
	 */
	private void ensureWidgetInitialized() {
		if (widgetInitializing || widgetInitialized) {
			return;
		}

		widgetInitializing = true;

		asyncOnInitialize(new AsyncCallback<Widget>() {
			public void onFailure(Throwable reason) {
				widgetInitializing = false;
				Window.alert("Failed to download code for this widget ("
						+ reason + ")");
			}

			public void onSuccess(Widget result) {
				widgetInitializing = false;
				widgetInitialized = true;

				Widget widget = result;
				if (widget != null) {
					view.setExample(widget);
				}
				onInitializeComplete();
			}
		});
	}

}
